﻿#region Includes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using OpenTK.Graphics.OpenGL;
#endregion

namespace GingerGL.Graphics
{
    //------------------------------------------------------------//
    /// <summary>
    /// Stores indices to vertex in a buffer.
    /// </summary>
    public class IndexBuffer : IDisposable
    {
        //--------------------------------------------------------//
        #region Variables
        /// <summary>
        /// Gets TRUE if IndexBuffer is Commited to memory
        /// </summary>
        public bool IsCommited { get; private set; }
        /// <summary>
        /// Gets the OopenGL Buffer Id
        /// </summary>
        public int IndexBufferId { get; set; }
        private List<int> mIndexBuffer;
        private int mCount;
        #endregion
        //--------------------------------------------------------//
        #region Properties
        /// <summary>
        /// Gets the count of indices in the buffer.
        /// </summary>
        /// <value>Count of indices.</value>
        public int Count { get { return this.mCount; } }
        /// <summary>
        /// Indexor
        /// </summary>
        public int this[int ndex] { get { return mIndexBuffer[ndex]; } }
        #endregion
        //--------------------------------------------------------//
        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="IndexBuffer"/> class.
        /// </summary>
        public IndexBuffer() 
        { 
            this.mIndexBuffer = new List<int>();
            this.mCount = 0;
            this.IndexBufferId = -1;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="IndexBuffer"/> class.
        /// </summary>
        public IndexBuffer(int[] indices) : this() { this.Initialize(indices); }
        #endregion
        //--------------------------------------------------------//
        #region Methods
        /// <summary>
        /// This initializes the index buffer and commits information to video ram
        /// </summary>
        private void Initialize(int[] indices)
        {
            this.Write(indices);
            this.Commit();
        }
        /// <summary>
        /// Clear local cache
        /// </summary>
        public void Clear() { if(mIndexBuffer!=null) mIndexBuffer.Clear(); mIndexBuffer = null; }
        /// <summary>
        /// clears IndexusBuffer from Memory
        /// </summary>
        public void Invalidate()
        {
            int id = IndexBufferId;
            if (IndexBufferId != -1)
                GL.DeleteBuffers(1, ref id);
            IsCommited = false;
            this.mCount = 0;
        }
        /// <summary>
        /// Commits IndexBuffer to Memory
        /// </summary>
        public void Commit()
        {
            if (mIndexBuffer == null) return;                   //dblcheck
            if (mIndexBuffer.Count < 1) return;                 //dblcheck
            int id;
            GL.GenBuffers(1, out id);                           //create buffers
            IndexBufferId = id;
            int bufferSize;                                     //passing in the requests buffer and will save the save VBO information
            GL.BindBuffer(                                      // Bind current context to Array Buffer ID
                BufferTarget.ElementArrayBuffer, 
                IndexBufferId);
            GL.BufferData(                                      // Send data to buffer
                BufferTarget.ElementArrayBuffer, 
                (IntPtr)(mIndexBuffer.Count * sizeof(int)), 
                mIndexBuffer.ToArray(), 
                BufferUsageHint.StaticDraw);
            GL.GetBufferParameter(                              // Validate that the buffer is the correct size
                BufferTarget.ElementArrayBuffer, 
                BufferParameterName.BufferSize, 
                out bufferSize);

            if (mIndexBuffer.Count * sizeof(int) != bufferSize)  //dblcheck
                throw new ApplicationException("Element array not uploaded correctly");

            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);  //Clear the buffer Binding

            this.IsCommited = true;                             //ready
            this.mCount = mIndexBuffer.Count;                   //remember how many elements
        }
        /// <summary>
        /// Gets the index of the vertex at the specified offset.
        /// </summary>
        /// <param name="offset">The offset in the buffer.</param>
        /// <returns>Index to a vertex.</returns>
        public int GetVertexIndex(int offset) { return this.mIndexBuffer[offset]; }
        /// <summary>
        /// Writes the specified index to the index buffer.
        /// </summary>
        /// <param name="index">The index to write.</param>
        public void Write(int index) { this.mIndexBuffer.Add(index); }
        /// <summary>
        /// Writes the specified index to the index buffer.
        /// </summary>
        /// <param name="index">The index to write.</param>
        public void Write(int[] index) { this.mIndexBuffer.AddRange(index); }
        /// <summary>
        /// Disposes of this class and video memory
        /// </summary>
        public void Dispose() { this.Invalidate(); this.Clear(); }
        #endregion
        //--------------------------------------------------------//
    }
    //------------------------------------------------------------//
}
